#  include <GL/gl.h>
#  include <GL/glu.h>
#  include <GL/glut.h>
#include <GL/glx.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <math.h>

unsigned int vindusbredde;
unsigned int vindushoyde;
double komp_bredde, komp_hoyde;
double maks_iter;
double komp_re_min;
double komp_im_min;
double re_punkt;
double im_punkt;
bool beregnet_mandelbrot;
bool **mandelbrot;



void init()
{
  /* set clear color to white */
  glClearColor (1.0, 1.0, 1.0, 0.0);
  
  /* set fill color to green */
  glColor3f(0.0, 0.0, 0.0);
  
  /* set up standard orthogonal view with clipping */
  /* box as cube of side 2 centered at origin */
  /* This is default view and these statement could be removed */
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrtho(0.0, vindusbredde, 0.0, vindushoyde, 0.0, vindushoyde);
}


bool sjekk_punkt(double xr, double xi, double cr, double ci){
  unsigned int iterasjoner;
  bool er_inne = true;
  for(iterasjoner = 0; iterasjoner < maks_iter; iterasjoner ++){
    double xr2 = xr * xr;
    double xi2 = xi * xi;    
    if(xr2 + xi2 > 4.0){
      er_inne = false;
      break;
    }
    xi = 2 * xr * xi + ci;
    xr = xr2 - xi2 + cr;
  }
  return er_inne;
}


void beregn_mandelbrot(){
  int r, i;
 
  for(r = 0; r < vindusbredde; r++){
    double c_re =  r * re_punkt + komp_re_min;
    for(i = 0; i < vindushoyde; i++){
      double c_im = i * im_punkt + komp_im_min;
      mandelbrot[r][i] = sjekk_punkt(c_re, c_im, c_re, c_im);
    }
  }
}

void tegn_mandelbrot(){
  int r, i;
  glBegin(GL_POINTS);
  for(r = 0; r < vindusbredde; r++){
    for(i = 0; i < vindushoyde; i++){
      if(mandelbrot[r][i])
	glVertex2f(r, i);
    }
  }
  glEnd();
}


GLvoid display(GLvoid){
  if(!beregnet_mandelbrot){
    beregn_mandelbrot();
    beregnet_mandelbrot = true;
  }
  glClear(GL_COLOR_BUFFER_BIT);
  tegn_mandelbrot();
  glFlush();
}

/**
    Her har vi definert hvor mange iterasjoner programmet skal regne ut, definert ved bredden av vinduet.
    I tillegg definerer vi høyden på det komplekse planet ved hjelp av størrelsen på vinduet.
    Til slutt definerer vi forholdet mellom pikslene på skjermen og verdiene i det komplekse planet.
**/

void oppdater_konstanter(){
  maks_iter = vindusbredde / 10;
  komp_hoyde = komp_bredde * vindushoyde / vindusbredde;
  re_punkt = komp_bredde / vindusbredde;
  im_punkt = komp_hoyde / vindushoyde;
}

/**
    Dette avsnittet forteller hvor i det komplekse planet vi befinner oss, og hvor mye av det som skal vises i 
    vinduet. I tillegg defineres hvor stort vinduet skal være, noe som vil påvirke antall iterasjoner.
**/
GLvoid init_verdier(){
  unsigned int i;
  komp_bredde = 2.0;
  komp_re_min = -1.5;
  komp_im_min = -1.0;
  vindusbredde = 750;
  vindushoyde = 750;
  mandelbrot = calloc(vindusbredde, sizeof(bool*));
  for(i = 0; i < vindusbredde; i++)
    mandelbrot[i] = calloc(vindushoyde, sizeof(bool));
  oppdater_konstanter();
}
 

int main(int argc, char** argv) {
  init_verdier();
  glutInit(&argc,argv);
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(vindusbredde,vindushoyde);
  glutInitWindowPosition(0,0);
  glutCreateWindow("mandelbrot");
  glutDisplayFunc(display);
  /*glutReshapeFunc(endre_skjerm);
  glutMouseFunc(knappTrykk);
  glutSpecialFunc(pilTrykk);
  glutKeyboardFunc(tasteTrykk);*/
  init();
  glutMainLoop();
  return 0;
}

